package core

import (
	"fmt"

	"gitee.com/caivega/easm/util"
	"github.com/ethereum/go-ethereum/common"
)

var (
	PRINT_ADDRESS common.Address
)

const (
	ASSERT_TEST   string = "@test"
	ASSERT_INPUT  string = "@input"
	ASSERT_RETURN string = "@return"
)

const (
	BLOCK_IF     string = "if"
	BLOCK_ELSE   string = "else"
	BLOCK_ELSEIF string = "elseif"
	BLOCK_ENDIF  string = "endif"

	BLOCK_FOR    string = "for"
	BLOCK_ENDFOR string = "endfor"

	BLOCK_BODY string = "body"
)

type BlockItemType int

const (
	BLOCK_ITEM_IF  BlockItemType = 1
	BLOCK_ITEM_FOR BlockItemType = 2
)

var BLOCK_ITEM_TYPE = util.CreateType()

const (
	SEPERATOR_LINE string = "@"
)

type LabelType int

const (
	LABEL_MODULE_NAME    string = "module"
	LABEL_SECTION_NAME   string = "section"
	LABEL_LABEL_NAME     string = "label"
	LABEL_FUNCTION_NAME  string = "function"
	LABEL_BLOCK_NAME     string = "block"
	LABEL_CONST_NAME     string = "const"
	LABEL_VAR_NAME       string = "var"
	LABEL_SLOT_NAME      string = "slot"
	LABEL_LOCAL_NAME     string = "local"
	LABEL_STACK_NAME     string = "stack"
	LABEL_CALL_NAME      string = "call"
	LABEL_PUSH_NAME      string = "push"
	LABEL_STORE_NAME     string = "store"
	LABEL_RETURN_NAME    string = "return"
	LABEL_PARAMETER_NAME string = "parameter"
	LABEL_CALL_DATA_NAME string = "call_data"
	LABEL_OP_NAME        string = "op"
	LABEL_PRINT_NAME     string = "print"
	LABEL_VALUE_NAME     string = "value"
	LABEL_IFNOT_NAME     string = "ifnot"

	LABEL_MODULE    LabelType = 1
	LABEL_SECTION   LabelType = 2
	LABEL_LABEL     LabelType = 3
	LABEL_FUNCTION  LabelType = 4
	LABEL_BLOCK     LabelType = 5
	LABEL_CONST     LabelType = 6
	LABEL_VAR       LabelType = 7
	LABEL_SLOT      LabelType = 8
	LABEL_LOCAL     LabelType = 9
	LABEL_STACK     LabelType = 10
	LABEL_CALL      LabelType = 11
	LABEL_PUSH      LabelType = 12
	LABEL_STORE     LabelType = 13
	LABEL_RETURN    LabelType = 14
	LABEL_PARAMETER LabelType = 15
	LABEL_CALL_DATA LabelType = 16
	LABEL_OP        LabelType = 17
	LABEL_PRINT     LabelType = 18
	LABEL_VALUE     LabelType = 19
	LABEL_IFNOT     LabelType = 20
)

var LABEL_TYPE = util.CreateType()

func LabelValue(s string) LabelType {
	t, ok := LABEL_TYPE.Create(s)
	if !ok {
		panic("error label type: " + s)
	}
	return LabelType(t)
}

func IsData(s string) bool {
	return s == LABEL_CONST_NAME || s == LABEL_VAR_NAME || s == LABEL_SLOT_NAME || s == LABEL_LOCAL_NAME
}

func (t LabelType) String() string {
	n, ok := LABEL_TYPE.String(int(t))
	if !ok {
		panic("unknown label type")
	}
	return n
}

type DataType int

const (
	DATA_STRING_NAME string = "string"
	DATA_INT_NAME    string = "int"

	DATA_STRING DataType = 1
	DATA_INT    DataType = 2
)

var DATA_TYPE = util.CreateType()

func DataValue(s string) DataType {
	t, ok := DATA_TYPE.Create(s)
	if !ok {
		panic("error data type: " + s)
	}
	return DataType(t)
}

func (t DataType) String() string {
	n, ok := DATA_TYPE.String(int(t))
	if !ok {
		panic("unknown data type")
	}
	return n
}

type AttributeType int

const (
	ATTRIBUTE_VALUE_NAME string = "value"
	ATTRIBUTE_SIZE_NAME  string = "size"

	ATTRIBUTE_VALUE DataType = 1
	ATTRIBUTE_SIZE  DataType = 2
)

var ATTRIBUTE_TYPE = util.CreateType()

func AttributeValue(s string) AttributeType {
	t, ok := ATTRIBUTE_TYPE.Create(s)
	if !ok {
		panic("error attribute type: " + s)
	}
	return AttributeType(t)
}

func (t AttributeType) String() string {
	n, ok := ATTRIBUTE_TYPE.String(int(t))
	if !ok {
		panic("unknown attribute type")
	}
	return n
}

type OperatorType int

const (
	OPERATOR_EQUALS_NAME           string = "=="
	OPERATOR_NOT_EQUALS_NAME       string = "!="
	OPERATOR_ANDS_NAME             string = "&&"
	OPERATOR_ORS_NAME              string = "||"
	OPERATOR_LARGER_NAME           string = ">"
	OPERATOR_LARGER_AND_EQUAL_NAME string = ">="
	OPERATOR_LESS_NAME             string = "<"
	OPERATOR_LESS_AND_EQUAL_NAME   string = "<="
	OPERATOR_EQUAL_NAME            string = "="
	OPERATOR_PLUSES_NAME           string = "++"
	OPERATOR_ADD_NAME              string = "+"
	OPERATOR_SUB_NAME              string = "-"
	OPERATOR_MUL_NAME              string = "*"
	OPERATOR_DIV_NAME              string = "/"
	OPERATOR_AND_NAME              string = "&"
	OPERATOR_OR_NAME               string = "|"
	OPERATOR_EXP_NAME              string = "^"
	OPERATOR_NOT_NAME              string = "!"

	OPERATOR_EQUALS           OperatorType = 10_1_1_05 //ab_c_d_ef(c:left parameter count(0 or 1), d:right, ef:weight)
	OPERATOR_NOT_EQUALS       OperatorType = 11_1_1_05
	OPERATOR_ANDS             OperatorType = 12_1_1_04
	OPERATOR_ORS              OperatorType = 13_1_1_03
	OPERATOR_LARGER           OperatorType = 14_1_1_00
	OPERATOR_LARGER_AND_EQUAL OperatorType = 15_1_1_00
	OPERATOR_LESS             OperatorType = 16_1_1_00
	OPERATOR_LESS_AND_EQUAL   OperatorType = 17_1_1_00
	OPERATOR_EQUAL            OperatorType = 18_1_1_09
	OPERATOR_PLUSES           OperatorType = 19_1_0_11
	OPERATOR_ADD              OperatorType = 20_1_1_00
	OPERATOR_SUB              OperatorType = 21_1_1_00
	OPERATOR_MUL              OperatorType = 22_1_1_01
	OPERATOR_DIV              OperatorType = 23_1_1_01
	OPERATOR_AND              OperatorType = 24_1_1_08
	OPERATOR_OR               OperatorType = 25_1_1_07
	OPERATOR_EXP              OperatorType = 26_1_1_06
	OPERATOR_NOT              OperatorType = 27_0_1_10
)

var OPERATOR_TYPE = util.CreateType()

func OperatorValue(s string) OperatorType {
	t, ok := OPERATOR_TYPE.Create(s)
	if !ok {
		panic("error operator type: " + s)
	}
	return OperatorType(t)
}

func (t OperatorType) String() string {
	n, ok := OPERATOR_TYPE.String(int(t))
	if !ok {
		fmt.Println(">>>", n, ok, int(t))
		panic("unknown operator type")
	}
	return n
}

func OperatorList() []OperatorType {
	types := make([]OperatorType, 0)
	list := OPERATOR_TYPE.List()
	for _, t := range list {
		types = append(types, OperatorType(t))
	}
	return types
}

func OperatorMap() map[string]OperatorType {
	m := make(map[string]OperatorType)
	list := OPERATOR_TYPE.List()
	for _, t := range list {
		ot := OperatorType(t)
		m[ot.String()] = ot
	}
	return m
}

func init() {
	PRINT_ADDRESS = common.BytesToAddress([]byte("print"))

	BLOCK_ITEM_TYPE.Register(BLOCK_IF, int(BLOCK_ITEM_IF))
	BLOCK_ITEM_TYPE.Register(BLOCK_FOR, int(BLOCK_ITEM_FOR))

	LABEL_TYPE.Register(LABEL_MODULE_NAME, int(LABEL_MODULE))
	LABEL_TYPE.Register(LABEL_SECTION_NAME, int(LABEL_SECTION))
	LABEL_TYPE.Register(LABEL_LABEL_NAME, int(LABEL_LABEL))
	LABEL_TYPE.Register(LABEL_FUNCTION_NAME, int(LABEL_FUNCTION))
	LABEL_TYPE.Register(LABEL_BLOCK_NAME, int(LABEL_BLOCK))
	LABEL_TYPE.Register(LABEL_CONST_NAME, int(LABEL_CONST))
	LABEL_TYPE.Register(LABEL_VAR_NAME, int(LABEL_VAR))
	LABEL_TYPE.Register(LABEL_SLOT_NAME, int(LABEL_SLOT))
	LABEL_TYPE.Register(LABEL_LOCAL_NAME, int(LABEL_LOCAL))
	LABEL_TYPE.Register(LABEL_STACK_NAME, int(LABEL_STACK))
	LABEL_TYPE.Register(LABEL_CALL_NAME, int(LABEL_CALL))
	LABEL_TYPE.Register(LABEL_PUSH_NAME, int(LABEL_PUSH))
	LABEL_TYPE.Register(LABEL_STORE_NAME, int(LABEL_STORE))
	LABEL_TYPE.Register(LABEL_RETURN_NAME, int(LABEL_RETURN))
	LABEL_TYPE.Register(LABEL_PARAMETER_NAME, int(LABEL_PARAMETER))
	LABEL_TYPE.Register(LABEL_CALL_DATA_NAME, int(LABEL_CALL_DATA))
	LABEL_TYPE.Register(LABEL_OP_NAME, int(LABEL_OP))
	LABEL_TYPE.Register(LABEL_PRINT_NAME, int(LABEL_PRINT))
	LABEL_TYPE.Register(LABEL_VALUE_NAME, int(LABEL_VALUE))
	LABEL_TYPE.Register(LABEL_IFNOT_NAME, int(LABEL_IFNOT))

	DATA_TYPE.Register(DATA_STRING_NAME, int(DATA_STRING))
	DATA_TYPE.Register(DATA_INT_NAME, int(DATA_INT))

	ATTRIBUTE_TYPE.Register(ATTRIBUTE_VALUE_NAME, int(ATTRIBUTE_VALUE))
	ATTRIBUTE_TYPE.Register(ATTRIBUTE_SIZE_NAME, int(ATTRIBUTE_SIZE))

	OPERATOR_TYPE.Register(OPERATOR_EQUALS_NAME, int(OPERATOR_EQUALS))
	OPERATOR_TYPE.Register(OPERATOR_NOT_EQUALS_NAME, int(OPERATOR_NOT_EQUALS))
	OPERATOR_TYPE.Register(OPERATOR_ANDS_NAME, int(OPERATOR_ANDS))
	OPERATOR_TYPE.Register(OPERATOR_ORS_NAME, int(OPERATOR_ORS))
	OPERATOR_TYPE.Register(OPERATOR_LARGER_NAME, int(OPERATOR_LARGER))
	OPERATOR_TYPE.Register(OPERATOR_LARGER_AND_EQUAL_NAME, int(OPERATOR_LARGER_AND_EQUAL))
	OPERATOR_TYPE.Register(OPERATOR_LESS_NAME, int(OPERATOR_LESS))
	OPERATOR_TYPE.Register(OPERATOR_LESS_AND_EQUAL_NAME, int(OPERATOR_LESS_AND_EQUAL))
	OPERATOR_TYPE.Register(OPERATOR_EQUAL_NAME, int(OPERATOR_EQUAL))
	OPERATOR_TYPE.Register(OPERATOR_PLUSES_NAME, int(OPERATOR_PLUSES))
	OPERATOR_TYPE.Register(OPERATOR_ADD_NAME, int(OPERATOR_ADD))
	OPERATOR_TYPE.Register(OPERATOR_SUB_NAME, int(OPERATOR_SUB))
	OPERATOR_TYPE.Register(OPERATOR_MUL_NAME, int(OPERATOR_MUL))
	OPERATOR_TYPE.Register(OPERATOR_DIV_NAME, int(OPERATOR_DIV))
	OPERATOR_TYPE.Register(OPERATOR_AND_NAME, int(OPERATOR_AND))
	OPERATOR_TYPE.Register(OPERATOR_OR_NAME, int(OPERATOR_OR))
	OPERATOR_TYPE.Register(OPERATOR_EXP_NAME, int(OPERATOR_EXP))
	OPERATOR_TYPE.Register(OPERATOR_NOT_NAME, int(OPERATOR_NOT))
}
